【Spring Bean生命周期】聊透扩展点的流程及应用

您所在的位置:网站首页 springboot 扩展点 【Spring Bean生命周期】聊透扩展点的流程及应用

【Spring Bean生命周期】聊透扩展点的流程及应用

2024-06-06 12:14| 来源: 网络整理| 查看: 265

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天 Spring Bean生命周期,从入门到进阶: 【Spring Bean生命周期】小白也能看懂的入门篇 【Spring Bean生命周期】高手如何解读源码(一) - 资源的扫描和注册 【Spring Bean生命周期】高手如何解读源码(二) - Bean的生命周期

  Spring之所以具备如此强大的生态,扩展能力强是很重要的一方面,这得益于它本身提供了非常多的扩展点。本节我们针对Spring生命周期中涉及到的扩展点来看一看,究竟它们是何方神圣?

1 扩展点流程

  Spring生命周期的主要扩展点如图:

image.png

2 扩展点解析及应用场景 ApplicationContextInitializer接口

  用于在执行refresh()之前初始化ConfigurationApplicationContext的回调接口。 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,注册属性源或根据上下文环境激活配置文件。 比如:Spring boot就写了几个ApplicationContextInitializer的实现类,常见作用:

ConfigurationWarningsApplicationContextInitializer:对于一般配置错误在日志中作出警告 ContextIdApplicationContextInitializer:设置ApplicationContext#getId()所获取的ID值,默认取spring.application.name属性值,没有配置时默认为 application DelegatingApplicationContextInitializer:使用环境属性context.initializer.classes指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做 ...... 实现方式如下 public class CustomApplicationContextInitializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext applicationContext) { // 打印当前方法类名和方法名,调试用 CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName()); } }

在配置文件中设置:

context.initializer.classes=com.zyq.demo.expand.CustomApplicationContextInitializer BeanDefinitionRegistryPostProcessor

  这个接口在读取项目中的beanDefinition之后执行,提供一个补充的扩展点。使用场景:可以在这里动态注册自己的beanDefinition,可以加载classpath之外的bean。

  比如我们在经常使用的mybatis的mapper和我们的feignClient,往往只需要定义接口而不需要写实现,那么这些实现的生成及注入就是在此处完成的。

@Component public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // 可以在此处自定义扫描mapper,并生成BeanDefinition信息,通过registry注册到容器中 RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Test001.class); registry.registerBeanDefinition("test001",rootBeanDefinition); CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName()); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName()); } } BeanFactoryPostProcessor

  这个接口是beanFactory的扩展接口,调用时机在spring在读取beanDefinition信息之后,实例化bean之前。在这个时机,用户可以通过实现这个扩展接口来自行处理一些东西,比如修改已经注册的beanDefinition的元信息。根据注释可知,此时只能承上(即修改元信息),坚决不允许启下(即触发实例化)

  实际工作中,自定义BeanFactoryPostProcessor的情况确实少,比如对敏感信息的解密处理,在数据库的连接配置中,用户名和密码都是明文配置的,这就存在泄漏风险,还有redis的连接配置、shiro的加密算法、rabbitmq的连接配置等等,凡是涉及到敏感信息的,都需要进行加密处理,信息安全非常重要。

  配置的时候以密文配置,在真正用到之前在spring容器中进行解密,然后用解密后的信息进行真正的操作。

@Component public class CustomBeanFactoryAware implements BeanFactoryPostProcessor { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { MutablePropertySources propSources = environment.getPropertySources(); StreamSupport.stream(propSources.spliterator(), false) .filter(ps -> ps instanceof OriginTrackedMapPropertySource) .collect(Collectors.toList()) .forEach(ps -> convertPropertySource((PropertySource) ps)); LOGGER.info("敏感信息解密完成....."); } } InstantiationAwareBeanPostProcessor

  InstantiationAwareBeanPostProcessor接口继承BeanPostProcessor接口,它内部提供了3个方法,再加上BeanPostProcessor接口内部的2个方法,所以实现这个接口需要实现5个方法。可以称作是Spring生命周期的大宝剑!Spring的核心功能之一AOP就是基于它来实现的。

@Component public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { //在实例化之前被调用 @Override public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { if(beanClass == TargetClass.class){ //使用cglib实现动态代理AOP Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(beanClass); enhancer.setCallback(new TargetClassProxy()); TargetClass proxyClass = (TargetClass)enhancer.create(); return proxyClass; } return null; } } BeanNameAware

  这个类也是Aware扩展的一种,触发点在bean的初始化之前,也就是postProcessBeforeInitialization之前,这个类的触发点方法只有一个:setBeanName。使用场景:用户可以扩展这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行修改这个beanName的值。整个约定了在编写服务代码时,就可以知道客户端调用时的bean名称,可在此处打标签,用于一些特殊bean的捕获。

public class CustomBeanNameAware implements BeanNameAware { @Override public void setBeanName(String name) { System.out.println("在这里修改Bean名称"); } } @PostConstruct

  @PostConstruct这个并不算一个扩展点,其实就是一个标注。从Java EE5规范开始,Servlet中增加的注解,被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是在postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前。

  使用场景:如果想在生成对象时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

spring自带的@schedule,没有开关,项目启动总会启动一个线程;做项目的时候就使用Java的timer,这个设置开关即可自由的控制,关闭的时候,不会启动线程;Java的timer也需要找到一个启动类,可以放到main函数里面启动,这样的话,代码的耦合性太高了,而使用PostConstruct是很干净的。 @PostConstruct public void init() { // 启动定时任务线程 } InitializingBean

  InitializingBean的作用是Bean注入到Spring容器且初始化后,执行特定业务化的操作。Spring允许容器中的Bean,在Bean初始化完成后或者Bean销毁前,执行特定业务化的操作,常用的实现方式有以下三种:

通过实现InitializingBean/DisposableBean接口来处理初始化后/销毁前的操作; 通过标签的init-method/destroy-method属性处理初始化后/销毁前的操作; 在指定方法上加上@PostConstruct或@PreDestroy注解来处理初始化后/销毁前的操作。

在spring初始化bean的时候,如果该bean是实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertiesSet方法,然后在调用init-method中指定的方法

public class CustomInitializingBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { if (StringUtils.isEmpty(path)) { log.error("PLEASE SET THE RULE'S PATH: (spring.drools.path = XXX)."); } } } BeanPostProcessor

  在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。

public interface BeanPostProcessor { /** * Apply this {@code BeanPostProcessor} to the given new bean instance before any bean * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. * The returned bean instance may be a wrapper around the original. */ @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * Apply this {@code BeanPostProcessor} to the given new bean instance after any bean * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. * The returned bean instance may be a wrapper around the original. */ @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }

从代码可以看出来,返回的时经过处理后的Bean,这种就很适合代理类的场景,如果需要实现类似AOP的逻辑,可以在此处理。

好了,上面就是涉及到Spring生命周期的一些主要扩展点,主要是帮助我们集合Spring生命周期来理解Spring的整体逻辑。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3